home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / fortran / f2c-stab.9 / f2c-stab / f2c-stabs / parcil.el < prev    next >
Encoding:
Text File  |  1996-03-31  |  15.6 KB  |  450 lines

  1. (require 'cl)
  2. (require 'cl-19)
  3.  
  4. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  5. ;;;
  6. ;;;  PARCIL - A Parser for C syntax In Lisp
  7. ;;;  version 0.1a
  8. ;;;
  9. ;;;  copyright (c) 1992 by Erann Gat, all rights reserved
  10. ;;;
  11. ;;;  Ported to elisp by Harvey Stein <abel@netvision.net.il>, and
  12. ;;;  eventually <hjstein@netvision.net.il>.
  13. ;;;  Modified in various minor ways for handling Fortran.
  14. ;;;  Added function unparcil - the inverse of parcil.
  15. ;;;
  16. ;;;  This program is free software; you can redistribute it and/or modify
  17. ;;;  it under the terms of the GNU General Public License as published by
  18. ;;;  the Free Software Foundation.
  19. ;;;
  20. ;;;  This program is distributed in the hope that it will be useful,
  21. ;;;  but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. ;;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23. ;;;  GNU General Public License for more details.
  24. ;;;
  25. ;;;  You should have received a copy of the GNU General Public License
  26. ;;;  along with this program; if not, write to the Free Software
  27. ;;;  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  28. ;;;
  29. ;;;
  30. ;;;  This is a very preliminary release and almost certainly contains bugs.
  31. ;;;  Please send bug reports and comments to:
  32. ;;;  Erann Gat
  33. ;;;  JPL MS 525-3660
  34. ;;;  4800 Oak Grove Drive
  35. ;;;  Pasadena, CA 91109
  36. ;;;  (818) 306-6176
  37. ;;;  gat@robotics.jpl.nasa.gov or gat@aig.jpl.nasa.gov
  38. ;;;
  39. ;;;  Revision history:
  40. ;;;  v0.1a - Initial release
  41. ;;;
  42.  
  43. ;;;  PARCIL is a parser for a subset of the syntax for the C programming
  44. ;;;  language.  PARCIL is written in Common Lisp, making it potentially
  45. ;;;  a useful building block for user interfaces for people who do not
  46. ;;;  like prefix syntax.
  47. ;;;
  48. ;;;  PARCIL is a recursive descent parser optimized to parse C.  This makes it
  49. ;;;  fairly brittle and difficult to modify.  However, it does make it fairly
  50. ;;;  fast, and it also allows the parser to deal with lots of C idiosyncrasies
  51. ;;;  which are difficult to implement in general-purpose parsers, e.g. operator
  52. ;;;  precedence, prefix and postfix operators, etc.
  53. ;;;
  54. ;;;  NOTE:  While PARCIL is designed to be a component in user interfaces for
  55. ;;;  people who are not regular LISP users, it is probably not usable for that
  56. ;;;  purpose as-is.  There are two major problems with it.  First, it is incomplete.
  57. ;;;  It currently includes no support for any high-level C construct (i.e. it
  58. ;;;  implements the syntax described in the original Kernighan and Richie book,
  59. ;;;  section 18.1).  The second problem is that PARCIL is so faithful to C syntax
  60. ;;;  that it can easily fool the unwary into believing that they are writing C code
  61. ;;;  when in fact they are writing LISP code, only with a different syntax.  You
  62. ;;;  need a fairly deep understanding of the distinction between syntax and
  63. ;;;  semantics in order to use PARCIL.  The main stumbling block to its use by
  64. ;;;  beginners is that PARCIL does very little error checking.  Thus, many errors
  65. ;;;  which should be detected by PARCIL are passed on and caught by LISP.  The
  66. ;;;  resulting error messages can be very cryptic if you don't know what's going
  67. ;;;  on.
  68. ;;;
  69. ;;;  PHILOSOPHICAL RANT:  Infix notation is a blight on the intellectual landscape.
  70. ;;;  It is confusing to read, difficult to parse, and to avoid ambiguity must rely
  71. ;;;  on precedence rules that are hopelessly obscure.  People who prefer infix
  72. ;;;  notation do so only because they have been indoctrinated to it since
  73. ;;;  childhood and do not have the intellectual strength to break free.  It is
  74. ;;;  far better to convince people to use prefix notation, with its easy to read
  75. ;;;  and easy to parse, unambiguous syntax, than to provide them with crutches
  76. ;;;  such as PARCIL which perpetuate such evils as infix, prefix and postfix unary
  77. ;;;  operators.  (In C, "x++*++****y" is a legal expression, and the first * doesn't
  78. ;;;  mean the same thing as all the other *'s.)  Nevertheless, I acknowledge the
  79. ;;;  reality that infix and C are here to stay, and that is why I have written
  80. ;;;  PARCIL.  But that doesn't mean I have to like it.
  81. ;;;
  82. ;;;  USER'S GUIDE:
  83. ;;;
  84. ;;;  The top-level function is called PARCIL.  Pass a string consisting of a C
  85. ;;;  expression (not a command!) to PARCIL and it will return a parsed version.
  86. ;;;  For example:
  87. ;;;
  88. ;;;  (parcil "x=y*sin(pi/2.7)") ==> (SETF X (* Y (SIN (/ PI 2.7))))
  89. ;;;
  90. ;;;  PARCIL supports all syntax defined in section 18.1 of the original Kernighan
  91. ;;;  and Ritchie book, plus all C numerical syntax including floats and radix
  92. ;;;  syntax (i.e. 0xnnn, 0bnnn, and 0onnn).  In addition, PARCIL supports multiple
  93. ;;;  array subscripts.  There is also a preliminary version of {} blocks, but it
  94. ;;;  doesn't quite do the right thing.  Parcil also allows strings to be delimited
  95. ;;;  using single quotes as well as double quotes (but you must use the same type
  96. ;;;  to close the string as you did to open it).
  97.  
  98. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  99. ;;;
  100. ;;;  Program starts here
  101. ;;;
  102.  
  103. ;;;  Misc. utilities
  104. ;;;
  105. ;;;(defmacro iterate (name args &rest body)
  106. ;;;  `(labels ((,name ,(mapcar #'car args) ,@body))
  107. ;;;     (,name ,@(mapcar #'cadr args))))
  108.  
  109. (defmacro parcil:iterate (name args &rest body)
  110.   (` (labels (((, name) (,(mapcar 'car args)) (,@ body)))
  111.        ( (, name) (,@ (mapcar 'cadr args))))))
  112.           
  113.  
  114. ;;(defmacro while (condition &body body)
  115. ;;  `(parcil:iterate loop () (if ,condition (progn ,@body (loop)))))
  116.  
  117. ;;;  Crufty pseudo-text-file interface.  Don't let impressionable young minds
  118. ;;;  see this code.
  119. ;;;
  120. (defvar *the-string* "")
  121. (defvar *the-pointer* 0)
  122.  
  123. (defun parcil:parse-init (s)
  124.   (setq *the-string* s)
  125.   (setq *the-pointer* 0))
  126.  
  127. (defun parcil:eof (&optional offset)
  128.   (if (null offset) (setq offset 0))
  129.   (>= (+ *the-pointer* offset) (length *the-string*)))
  130.  
  131. (defun parcil:peek (&optional offset)
  132.   (if (null offset) (setq offset 0))
  133.   (if (parcil:eof offset)
  134.     nil
  135.     (elt *the-string* (+ *the-pointer* offset))))
  136.  
  137. (defun parcil:readc ()
  138.   (prog1 (parcil:peek) (incf *the-pointer*)))
  139.  
  140. ;;;  The PARCIL tokenizer.  (FSA?  What's an FSA?)
  141. ;;;
  142. (defun parcil:letter (c)
  143.   (and (numberp c)
  144.        (or (and (<= ?a c) (<= c ?z))
  145.        (and (<= ?A c) (<= c ?Z)))))
  146.  
  147. (defun parcil:digit (c)
  148.   (and (numberp c) (<= ?0 c) (<= c ?9)))
  149.  
  150. (defun parcil:ident (thing)
  151.   (and thing
  152.        (symbolp thing)
  153.        (parcil:letter (elt (symbol-name thing) 0))))
  154.  
  155. (defvar *binary-ops*
  156.   '((\. ->) (* / %) (+ -) (<< >>) (< > <= >=) (== !=) (&) (^) (\|) (&&) (\|\|) (:)
  157.     (= += -= *= /= %= &= ^= \|= >>= <<=)))
  158.  
  159. ;;; Any binary operator in this alist will be renamed in the parsed version.
  160. (defvar *binop-alist*
  161.   '((\. . struct-ref) (= . setf) (% . mod) (<< . ashl) (>> . ashr)
  162.     (& . logand) (^ . logxor) (\| . logior) (&& . and) (\|\| . or)))
  163.  
  164. (defvar *binop-inv-alist* 
  165.   (mapcar (lambda (x) (cons (cdr x) (car x)))
  166.       *binop-alist*))
  167.  
  168. (defun parcil:binop (s)
  169. ;; Was:
  170. ;; (member s *binary-ops* :test #'member)
  171.   (let ((l *binary-ops*)
  172.     (found ()))
  173.     (while (and l (not found))
  174.       (if (member s (car l))
  175.       (setq found t)
  176.     (setq l (cdr l))))
  177.     l))
  178.  
  179. (defun parcil:assignop (s)
  180.  (member s (car (last *binary-ops*))))
  181.  
  182.  
  183. (defun parcil:priority (s)
  184. ;;  (let ( (p (position s *binary-ops* :test #'member)) )
  185. ;;    (and p (- 40 p)))
  186.   (let ((p (parcil:binop s)))
  187.     (and p (- 40 (- (length *binary-ops*)
  188.             (length p))))))
  189.  
  190.  
  191. (defun parcil:translate-binop (op) (or (cdr (assoc op *binop-alist*)) op))
  192.  
  193. (defun parcil:eat-spaces ()
  194.   (do () ( (not (eql (parcil:peek) ? )) )
  195.     (parcil:readc)))
  196.  
  197. (defun parcil:syntax-error ()
  198.   (error "Syntax error near %s" (substring *the-string* (max 0 (1- *the-pointer*)))))
  199.  
  200. (defun parcil:parse-fixnum (&optional base)
  201.   (if (null base) (setq base 10))
  202.   (let ((cnt (string-match "[+-]?[0-9]+" *the-string* *the-pointer*)))
  203.     (if (null cnt)
  204.     (parcil:syntax-error))
  205.     (setq cnt (match-end 0))
  206.     (prog1
  207.     (string-to-int (substring *the-string* *the-pointer* cnt))
  208.       (setq *the-pointer* cnt))))
  209.  
  210. ;;; The following is an attempt to get parcil to properly parse ".7".
  211. ;;; Unfortunately, this seems to be a losing battle, because it seems
  212. ;;; to have already snarfed up the . before starting...
  213. (defun parcil:parse-fixnum-hack (&optional base)
  214.   (if (null base) (setq base 10))
  215.   (let ((cnt (string-match "^[+-]?[0-9]+\\.?[0-9]*" *the-string* *the-pointer*)))
  216.     (when (null cnt)
  217.       (setq cnt (string-match "^[+-]?\\.[0-9]+" *the-string* *the-pointer*))
  218.       (if (null cnt)
  219.       (parcil:syntax-error)))
  220.     (setq cnt (match-end 0))
  221.     (prog1
  222.     (string-to-int (substring *the-string* *the-pointer* cnt))
  223.       (setq *the-pointer* cnt))))
  224.  
  225. (defun parcil:parse-atom ()
  226.   (parcil:eat-spaces)
  227.   (if (parcil:eof)
  228.     nil
  229.     (let ( (c (parcil:peek)) )
  230.       (cond ( (parcil:letter c) (parcil:parse-symbol) )
  231.             ( (eql c ?0) (if (parcil:letter (parcil:peek 1))
  232.                             (parcil:parse-radix-integer)
  233.                             (parcil:parse-number)) )
  234.             ( (parcil:digit c) (parcil:parse-number) )
  235.             ( (or (eql c ?") (eql c ?')) (parcil:parse-string c) ) ;; Stick a " here to fool emacs hilight package.
  236.             (t (parcil:parse-operator))))))
  237.  
  238. (defun parcil:parse-symbol ()
  239.   (intern
  240.    (downcase
  241.     (let ((s ""))
  242.       (while (let ( (c (parcil:peek)) ) (and c (or (parcil:letter c) (parcil:digit c) (eql c ?_))))
  243.         (setq s (concat s (char-to-string (parcil:readc)))))
  244.       s))))
  245.  
  246. (defun parcil:parse-radix-integer ()
  247.   (parcil:readc)
  248.   (parcil:parse-fixnum (let ((c (parcil:readc)))
  249.           (cond ((= c ?x) 16)
  250.             ((= c ?o) 8)
  251.             ((= c ?b) 2)))))
  252.  
  253. (defun parcil:parse-number ()
  254.   (let* ( (n1 (parcil:parse-fixnum))
  255.           (c (parcil:peek))
  256.       (d 0.1))
  257.     (cond ((eql c ?.)
  258.        (parcil:decimal d n1 c))
  259.       ((or (eql c ?e) (eql c ?E) (eql c ?d) (eql c ?D))
  260.        (parcil:expt d n1 c))
  261.       (t n1))))
  262.  
  263. (defun parcil:decimal (d n1 c)
  264.   (parcil:readc)
  265.   (let ( (c (parcil:peek)) )
  266.     (cond ((parcil:digit c)
  267.        (incf n1 (* d (- c ?0)))
  268.        (setq d (/ d 10))
  269.        (parcil:decimal d n1 c))
  270.       ((or (eql c ?e) (eql c ?E) (eql c ?d) (eql c ?D))
  271.        (parcil:expt d n1 c))
  272.       (t 
  273.        n1))))
  274.  
  275. (defun parcil:expt (d n1 c)
  276.   (parcil:readc)
  277.   (let ( (e (parcil:parse-fixnum)) )
  278.     (* n1 (expt 10 e))))
  279.  
  280. (defun parcil:parse-string (terminator)
  281.   (parcil:readc)
  282.   (let ((s ""))
  283.     (parcil:iterate parcil:loop ()
  284.          (let ( (c (parcil:readc)) )
  285.            (when (eql c terminator) (return-from parcil:loop s))
  286.            (setq s (concat s c))
  287.            (parcil:loop)))))
  288.  
  289. ;;(defun parcil:parse-string (terminator)
  290. ;;  (parcil:readc)
  291. ;;  (parcil:parse-string-aux terminator "" (parcil:readc)))
  292.  
  293. ;;(defun parcil:parse-string-aux (terminator s c)
  294. ;;  (cond ((eql c terminator)
  295. ;;     s)
  296. ;;    (t (parcil:parse-string-aux terminator (concat s c) (parcil:readc)))))
  297.  
  298. (defun parcil:parse-operator ()
  299.   (let* ( (c (intern (char-to-string (parcil:readc))))
  300.       (p (parcil:peek))
  301.           (s (intern (format  "%s%s" c (if p (char-to-string p) "")))))
  302.     (cond ( (member s '(<< >>))
  303.             (parcil:readc)
  304.             (if (eql (parcil:peek) ?=)
  305.               (intern (format  "%s%c" s (parcil:readc)))
  306.               s) )
  307.           ( (member s '(++ -- << >> -> <= >= != == &&
  308.                         += -= *= /= %= &= ^= \|= \|\|))
  309.             (parcil:readc)
  310.             s )
  311.           (t c))))
  312.  
  313. ;;;  Crufty interface to the tokenizer.
  314. ;;;
  315. (defvar *next*)
  316.  
  317. (defun parcil:scan ()
  318.   (setq *next* (parcil:parse-atom)))
  319.  
  320. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  321. ;;;
  322. ;;;  The recursive-descent parser.  Look Ma, no tables!
  323. ;;;
  324. (defun* parcil:parse-expression (&optional (priority -1))
  325.   (parcil:iterate parcil:loop ( (result (parcil:parse-term)) )
  326.     (let ( (op (parcil:translate-binop *next*))
  327.            (new-priority (parcil:priority *next*)) )
  328.       (cond
  329.        ( (parcil:assignop *next*) (parcil:scan) (list op result (parcil:loop (parcil:parse-term))) )
  330.        ( (and (parcil:binop *next*) (> new-priority priority))
  331.          (parcil:scan) (parcil:loop (list op result (parcil:parse-expression new-priority))) )
  332.        (t result)))))
  333.  
  334. (defun* parcil:parse-arglist (&optional (terminator '\)) (separator '\,))
  335.   (parcil:iterate parcil:loop ()
  336.     (cond ( (null *next*) (error "Missing ~S" terminator) )
  337.           ( (eq *next* terminator) (parcil:scan) nil )
  338.           (t (let ( (arg1 (parcil:parse-expression)) )
  339.                (unless (or (eq *next* separator) (eq *next* terminator))
  340.                  (parcil:syntax-error))
  341.                (if (eq *next* separator) (parcil:scan))
  342.                (cons arg1 (parcil:loop)))))))
  343.  
  344. ;;;  Any prefix unary operator included in this table will be renamed in the parsed
  345. ;;;  version.  (Postfix ++ and -- are handled specially, in PARCIL:PARSE-TERM.)
  346. (defvar *unary-op-alist*
  347.   '((* . deref) (& . address-of)
  348.     (- . -) (! . not) (~ . lognot)
  349.     (++ . incf) (-- . decf)))
  350.  
  351. (defvar *unary-op-inv-alist* 
  352.   (mapcar (lambda (x) (cons (cdr x) (car x)))
  353.       *unary-op-alist*))
  354.  
  355. ;;;  This function parses what K&R call primary expressions.  These include numbers,
  356. ;;;  variables, structure references, array references, and all unary operators.
  357. ;;;  Parsing of curly brackets is also stuck in here, though it probably shouldn't be.
  358. ;;;  The weird precedence rules make this a fairly hariy and brittle piece of code.
  359. ;;;
  360. (defun parcil:parse-term ()
  361.   (parcil:iterate parcil:loop ( (term (prog1 *next* (parcil:scan))) )
  362. ;;    (insert (format "Next term is: %s\n" *next*))
  363.     (cond
  364.      ( (numberp term) term )
  365.      ( (assoc term *unary-op-alist*)
  366.        (list (cdr (assoc term *unary-op-alist*)) (parcil:parse-term)) )
  367.      ( (eq term '\( )
  368.        (cons 'progn (parcil:parse-arglist)) )
  369.      ( (eq term '{)
  370.        (list* 'let '() (parcil:parse-arglist '} '\;)) )
  371.      ( (eq *next* '\( )
  372.        (parcil:scan)
  373.        (parcil:loop (cons term (parcil:parse-arglist))) )
  374.      ( (eq *next* '\[ )
  375.        (parcil:scan)
  376.        (parcil:loop (` (aref (, term) (,@ (parcil:parse-arglist '\]))))) )
  377.      ( (eq *next* '\.)
  378.        (parcil:loop (` (struct-ref (, term) (, (prog1 (parcil:scan) (parcil:scan)))))) )
  379.      ( (eq *next* '->)
  380.        (parcil:loop (` (-> (, term) (, (prog1 (parcil:scan) (parcil:scan)))))) )
  381.      ( (eq *next* '++)
  382.        (parcil:scan)
  383.        (parcil:loop (` (prog1 (, term) (incf (, term))))) )
  384.      ( (eq *next* '--)
  385.        (parcil:scan)
  386.        (parcil:loop (` (prog1 (, term) (decf (, term))))) )
  387.      (t
  388.       (if (and (atom term) (not (parcil:ident term)))
  389.         (parcil:syntax-error))
  390.       term))))
  391.  
  392. ;;; Useful for unparsing...
  393. (defun parcil:separate-with (s l)
  394.   (cond ((null l) "")
  395.     ((null (cdr l)) (format "%s" (car l)))
  396.     (t  (concat (format "%s%s" (car l) s)
  397.             (parcil:separate-with s (cdr l))))))
  398.  
  399. ;;;;;;;;;;;;;;;;;
  400. ;;;
  401. ;;;  The top level
  402. ;;;
  403. (defun parcil (s)
  404.   (parcil:parse-init s)
  405.   (parcil:scan)
  406.   (prog1
  407.     (parcil:parse-expression)
  408.     (if *next* (parcil:syntax-error)))) ; If there's stuff left over something went wrong.
  409.  
  410.  
  411. (defun unparcil (s)
  412.     (cond ((atom s)
  413.        (format "%s" s))
  414.       ((eq (car s) 'progn)
  415.        (if (null (cddr s))
  416.            (unparcil (cadr s))
  417.          (format "(%s)"
  418.              (parcil:separate-with "," (mapcar 'unparcil (cdr s))))))
  419.       ((eq (car s) 'prog1)
  420.        (cond ((eq (car (nth 2 s)) 'incf)
  421.           (format "(%s++)" (unparcil (nth 1 s))))
  422.          ((eq (car (nth 2 s)) 'decf)
  423.           (format "(%s--)" (unparcil (nth 1 s))))
  424.          (t (error "found prog1 with neither incf nor decf"))))
  425.       ((eq (car s) 'aref)
  426.        (format "(%s[%s])"
  427.            (unparcil (nth 1 s))
  428.            (parcil:separate-with "," (mapcar 'unparcil (cddr s)))))
  429.       ((and (assoc (car s) *unary-op-inv-alist*)
  430.         (null (cddr s)))
  431.        (format "(%s %s)" (cdr (assoc (car s) *unary-op-inv-alist*))
  432.            (unparcil (cadr s))))
  433.       ((assoc (car s) *binop-inv-alist*)
  434.        (format "(%s %s %s)"
  435.            (unparcil (nth 1 s))
  436.            (cdr (assoc (car s) *binop-inv-alist*))
  437.            (unparcil (nth 2 s))))
  438.       ((parcil:binop (car s))
  439.        (format "(%s %s %s)"
  440.            (unparcil (nth 1 s))
  441.            (car s)
  442.            (unparcil (nth 2 s))))
  443.       (t
  444.        (format "(%s(%s))"
  445.            (unparcil (nth 0 s))
  446.            (parcil:separate-with  ","
  447.             (mapcar 'unparcil (cdr s)))))))
  448.  
  449. (provide 'parcil)
  450.